package com.conversationboard.formatter;

import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.conversationboard.config.Configuration;
import com.conversationboard.config.ConnectionStrategy;
import com.conversationboard.database.Database;
import com.conversationboard.logger.Logger;
import com.conversationboard.model.Board;
import com.conversationboard.model.Boards;
import com.conversationboard.model.Thread;
import com.conversationboard.model.User;
import com.conversationboard.validate.InputChecker;

/**
 * Subsitute http(s) links for actual HTML links. Ensure we don't enter a loop by baling out after MAX_ITERATIONS. This runs *after* substitutions have been
 * made, so it is important it doesn't pick up http links inside tags that have already been transformed (i.e. links and images). It does this by using the
 * optional group at the beginning of the expression, which if this is found, I don't do the substitution. This works because the BBCode substitution ensures
 * only an explicit subset of HTML gets through, so we can be sure that all links look like href=" and src=".
 * 
 * @author Keith Watson
 */

public class AutoLinkFormatter {

	private static final int MAX_ITERATIONS = 50;
	private static final Pattern hyperlink = Pattern.compile("\\b(href=\"|src=\")?((?:http|https|ftp|ftps|sftp|nntp|file)://[-a-z0-9\\?=@~'#\\*\\+%,!\\$\\&\\\\._:/\\\\(\\\\)']*)\\b", (Pattern.CASE_INSENSITIVE | Pattern.DOTALL));
	private static final Pattern youTubeLink = Pattern.compile("http://(uk\\.|www\\.)?youtube.com/watch\\?v\\=(.*)");
	private static final Pattern internalHyperlink = Pattern.compile("ThreadSelectedControllerServlet.*boardid=([0-9]+)&threadid=([0-9]+)", (Pattern.CASE_INSENSITIVE | Pattern.DOTALL));
	private static final Pattern anchorTag = Pattern.compile("<a href=\"(.*)\">.*</a>", (Pattern.CASE_INSENSITIVE | Pattern.DOTALL));


	public static String format(String input, Board postingFromBoard, User user) throws IllegalMessageException {

		Matcher matcher = hyperlink.matcher(input);

		int count = 0;
		int youTubeCounter = 0;

		StringBuffer buffer = new StringBuffer(input.length());

		while (matcher.find()) {

			if (++count >= MAX_ITERATIONS) {
				Logger.log(new Date(), "AutolinkFormatter iterated 200 times with this input: " + input, user);
				throw new IllegalMessageException("Error processing message.");
			}

			String match = matcher.group(2);

			/* If the link has '<a' or '</a' after it, we don't want to match it, as it will be handled by the BBCode linker. */

			boolean bbCodeLink = (matcher.group(1) != null);

			if (bbCodeLink) {
				continue;
			}

			if (match != null) {

				if (InputChecker.containsIllegalCharacter(match) || InputChecker.containsIllegalLink(match)) {
					matcher.appendReplacement(buffer, "[Removed Illegal Link]");
				} else {

					/* If it's an internal URL, try to substitute the raw URL with the thread's title */

					String url = matcher.group(0);
					String title = url;
					Matcher internalHyperlinkMatcher = internalHyperlink.matcher(url);

					try {

						if (internalHyperlinkMatcher.find()) {

							int boardId = Integer.parseInt(internalHyperlinkMatcher.group(1));
							int threadId = Integer.parseInt(internalHyperlinkMatcher.group(2));

							Board linkBoard = Boards.getBoard(boardId);

							Thread thread = Thread.get(boardId, threadId);
							title = thread.getTitle();

							/* Don't show titles if it's a private board, unless you are posting a link from a
							 * private board to the same private board. */

							if ((postingFromBoard != null) && linkBoard.isPrivateBoard() && (linkBoard.getBoardId() != postingFromBoard.getBoardId())) {
								title = url;
							}

							if (InputChecker.containsIllegalCharacter(title) || InputChecker.containsIllegalLink(title)) {
								Logger.log(new Date(), "Illegal character in thread's title", user);
								title = url;
							}
						}
					} catch (Throwable t) {
						Logger.log(new Date(), "Failed to parse board ID and thread ID from URL to get the thread's title", user);
					}

					/* If it's a YouTube link, format appropriately, if not, just plumb in the link. */

					Matcher youTubeMatcher = youTubeLink.matcher(url);

					if (youTubeMatcher.find()) {

						youTubeCounter++;

						String youTubeId = youTubeMatcher.group(2);
						String output = "<object width=\"425\" height=\"355\"><param name=\"movie\" value=\"http://www.youtube.com/v/" + youTubeId + "&fs=1&hl=en\"></param><param name=\"wmode\" value=\"transparent\"></param><param name=\"allowFullScreen\" value=\"true\"></param><embed src=\"http://www.youtube.com/v/" + youTubeId + "&fs=1\" type=\"application/x-shockwave-flash\" wmode=\"transparent\" allowfullscreen=\"true\" width=\"425\" height=\"355\"></embed></object>";

						/* Get rid of anything beyond the maxImagesPerPostSetting */

						if (youTubeCounter > Configuration.getInstance().getMaxImagesPerPost()) {
							matcher.appendReplacement(buffer, "");
						} else {
							matcher.appendReplacement(buffer, output);
						}

					} else {

						title = Formatter.escapeHtml(title);
						matcher.appendReplacement(buffer, "<a href=\"" + url + "\">" + title + "</a>");
					}
				}

			} else {
				matcher.appendReplacement(buffer, matcher.group());
			}

		}

		matcher.appendTail(buffer);
		input = buffer.toString();

		return input;

	}


	/**
	 * Extracts the URL component of an anchor tag. <a href="This is what gets extracted"></a>
	 * 
	 * @param anchor the HTML tag.
	 * @return
	 */

	public static String extractUrl(String anchor) {

		Matcher matcher = anchorTag.matcher(anchor);

		String url = "";

		if (matcher.matches()) {
			url = matcher.group(1);
		}

		return url;
	}


	public static void main(String[] args) throws Exception {

		Database.url = "jdbc:mysql://localhost:3306/ilx4";
		Configuration.getInstance().setConnectionStrategy(ConnectionStrategy.StaticConnection);
		Configuration.load();

		System.out.println(AutoLinkFormatter.format("http://www.ilxor.com/ILX/ThreadSelectedControllerServlet?showall=true&bookmarkedmessageid=41&boardid=40&threadid=51288", null, User.getAnonymousUser()));

	}

}
